package com.kamistoat.meimeichat.meimeichatclient.socket;

import com.kamistoat.meimeichat.meimeichatclient.application.UIServiceConnector;
import com.kamistoat.meimeichat.meimeichatclient.config.NettyClientConstant;
import com.kamistoat.meimeichat.meimeichatclient.infrastructure.util.BeanUtil;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.InetSocketAddress;
import java.util.concurrent.Callable;

/**
 * TCP链接使用的Netty客户端。用于保持和服务器的链接
 */
public class NettyClient implements Callable<ChannelFuture> {

    private Logger logger = LoggerFactory.getLogger(NettyClient.class);

    // 作为客户端，其TCP链接只会唯一和服务器建立，所以只需要一个EventLoop即可，不需要像服务端额外一个Loop来处理新连接。
    private final NioEventLoopGroup workGroup = new NioEventLoopGroup();
    // 额外定义的 ChannelFuture 变量，用于记录 Bootstrap.connect() 返回的异步操作句柄，用以检测异步操作是否执行完毕/成功
    private ChannelFuture channelFuture;
    // NettyTCP链接地址
    private final String IP = NettyClientConstant.NETTY_SERVER_CONNECT_HOST;
    private final Integer PORT = NettyClientConstant.NETTY_SERVER_CONNECT_PORT;

    private UIServiceConnector uiServiceConnector;

    public NettyClient(UIServiceConnector uiServiceConnector) {
        this.uiServiceConnector = uiServiceConnector;
    }

    @Override
    public ChannelFuture call() {
        try {
            Bootstrap clientBootstrap = new Bootstrap();
            clientBootstrap
                    // 指定LoopGroup
                    .group(this.workGroup)
                    .channel(NioSocketChannel.class)
                    .option(ChannelOption.AUTO_READ, true)
                    // 指定ChannelInitializer，用于绑定 Handler
                    .handler(new NettyClientChannelInitializer(this.uiServiceConnector));
            // netty的connect库方法自动异步执行，记录其异步操作句柄，用以检测异步操作是否执行完毕/成功
            this.channelFuture = clientBootstrap.connect(new InetSocketAddress(IP, PORT)).syncUninterruptibly();

            // 不再像之前那样监听channel的关闭动作了，而是额外写一个 destroy()方法
            // channelFuture.channel().closeFuture().sync();
            // 保存与服务器的通信信道
            BeanUtil.addBean("Client2ServerChannel", this.channelFuture.channel());
        } catch (Exception exception) {
            logger.error("Netty Client Start Error: " + exception.getMessage());
        } finally {
            if (this.channelFuture != null && this.channelFuture.isSuccess()) {
                logger.info("Netty Client Start Success (RegardLess Connection Error)");
            } else {
                destroy();
                logger.error("Netty Client Closed");
            }
        }
        // 将bind()的异步操作句柄返回
        return this.channelFuture;
    }

    public boolean isActive() {
        return this.channelFuture.channel().isActive();
    }

    /**
     * 发生异常时，优雅关闭LoopGroup
     */
    public void destroy() {
        this.workGroup.shutdownGracefully();
    }

    /**
     * 用于外部获取 bind() 的异步操作句柄
     *
     * @return
     */
    public ChannelFuture channelFuture() {
        return this.channelFuture;
    }
}
